#pragma once

#include <QOpenGLTexture>

#include <QWidget>
#include <QObject>
#include <QUdpSocket>
#include <QTimer>
#include <cmath>
#include <array>
#include <fstream>
#include <mutex>

#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/visualization/pcl_visualizer.h>
#define M_PI 3.14159265358979323846

using namespace pcl::visualization;

struct LidarPoint
{
    short x{0};
    short y{0};
    short z{0};
    unsigned short Distance{0}; // 距离 = Distance × Dis Unit
    std::byte Reflectivity{0};  // 反射率 = Reflectivity × 1%   范围：0 ~ 255
    // double Confidence;   // 低置信度标记，即回波信号的可信程度。    取值：1（标记低置信度），0（正常）
};

class LidarListener : public QWidget
{
    Q_OBJECT

public:
    LidarListener(QWidget* parent, std::string port, std::string angle_correction);
    static constexpr int LidarPointsCount = 128 * 1200;

protected:
    friend class UdpReceiver;
    virtual void onLidarReceive(const int &port, const std::array<double, 3> &position, const double &rotation, const std::array<GLfloat, LidarPointsCount * 3> &_data) {};
    std::string _port;
    std::array<double, 3> _position;
    double _rotation;

private:
    template <size_t N>
    int16_t ToInt16(const std::array<std::byte, N> bytes, size_t startIndex);
    template <size_t N>
    int32_t ToInt32(const std::array<std::byte, N> bytes, size_t startIndex);
    template <size_t N>
    uint32_t ToUInt32(const std::array<std::byte, N> bytes, size_t startIndex);

    void ProcessData(QByteArray datagram);
    bool ReadPackageData(const std::vector<std::byte> bytes, const std::vector<std::tuple<double, double>> &lstFramesBound, std::vector<double> &lstAzimuth_Offset, std::vector<double> &lstElevation);
    bool GetPoints(std::array<std::byte, 512> bytes, double DisUnit, double azimuth, std::vector<std::tuple<double, double>> lstFramesBound, std::vector<double> lstAzimuth_Offset, std::vector<double> lstElevation);
    bool ReadAngleCorrectionFile(std::string path, std::vector<std::tuple<double, double>> &lstFramesBound, std::vector<double> &lstAzimuth_Offset, std::vector<double> &lstElevation);
    int lastFrameIndex{-1};
    int currentFrameIndex{-1};

    std::array<GLfloat, LidarPointsCount * 3> frameVertices0{0};
    std::array<GLfloat, LidarPointsCount * 3> frameVertices1{0};
    std::array<GLfloat, LidarPointsCount * 3> frameVertices2{0};

    std::array<LidarPoint, LidarPointsCount> frameData0;
    std::array<LidarPoint, LidarPointsCount> frameData1;
    std::array<LidarPoint, LidarPointsCount> frameData2;

    pcl::PointCloud<pcl::PointXYZI>::Ptr cloud0;
    pcl::PointCloud<pcl::PointXYZI>::Ptr cloud1;
    pcl::PointCloud<pcl::PointXYZI>::Ptr cloud2;

    std::vector<std::tuple<double, double>> lstFramesBound;
    std::vector<double> lstAzimuth_Offset;
    std::vector<double> lstElevation;

    std::shared_ptr<PCLVisualizer> pcl_viewer;
    std::mutex mtx_viewer;
    QTimer* timer;

private slots:
    void updatePointCloud();
};

// template <class T>
// class LidarCallback : public LidarListener
// {
// public:
//     LidarCallback(T *pObject, void (T::*pMethod)(const int &port, const std::array<double, 3> &position, const double &rotation, const std::array<GLfloat, LidarPointsCount * 3> &_data), const int &port, std::string angle_correction, std::array<double, 3> position, double rotation) : LidarListener(port, position, rotation, angle_correction), _pObject(pObject), _pMethod(pMethod) {}

// protected:
//     virtual void onLidarReceive(const int &port, const std::array<double, 3> &position, const double &rotation, const std::array<GLfloat, LidarPointsCount * 3> &_data) override { (_pObject->*_pMethod)(port, position, rotation, _data); }

// private:
//     T *_pObject;
//     void (T::*_pMethod)(const int &, const std::array<double, 3> &, const double &, const std::array<GLfloat, LidarPointsCount * 3> &);
// };

// class LidarProcesser
// {
// public:
//     LidarProcesser();
//     std::map<int, void *> dataListeners() { return _dataListeners; }

// protected:
//     std::map<int, void *> _dataListeners;
// };

// template <class T>
// class LidarManager : public LidarProcesser
// {
// public:
//     static constexpr int LidarPointsCount = 128 * 1200;
//     LidarManager(T *pObject, void (T::*pMethod)(const int &port, const std::array<double, 3> &position, const double &rotation, const std::array<GLfloat, LidarPointsCount * 3> &_data), UdpSetting setting) : _pObject(pObject), _pMethod(pMethod)
//     {
//         _ports = setting.ports;
//         for (int i = 0; i < _ports.size(); i++)
//         {
//             auto callback = new LidarCallback<T>(pObject, pMethod, _ports[i].toInt(), setting.angle_correction[i].toStdString(), setting.position[i], setting.rotation[i]);
//             _dataListeners[_ports[i].toInt()] = static_cast<void *>(callback);
//             // auto test = static_cast<LidarListener *>(_dataListeners[_ports[i].toInt()]);
//         }
//     };

// private:
//     T *_pObject;
//     void (T::*_pMethod)(const int &, const std::array<double, 3> &, const double &, const std::array<GLfloat, LidarPointsCount * 3> &);
//     QStringList _ports;
// };